﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;

namespace MeTalker.Securitys.Cryptography
{
    internal enum CryptographyMode
    {
        Default,
        ServiceProvider
    }
    /// <summary>
    /// AES对称加密解密类
    /// </summary>
    internal class AESHelper
    {
        internal byte[] IV { get; private set; }

        public CryptographyMode CryptographyMode
        {
            get;
            set;
        }
        /// <summary>
        /// 
        /// </summary>
        public AESHelper()
            : this(CryptographyMode.Default)
        {
        }

        public AESHelper(CryptographyMode cryptographyMode)
            : this("!@#)(&(&^^%$%$%^&*()*)(P+_()*^^$^&&^())(_)(^&%$%#%$#%)+Y*(()JI^)IO%", cryptographyMode)
        {
        }

        /// <summary>
        ///  构造
        /// </summary>
        /// <param name="byteKey"></param>
        public AESHelper(byte[] byteKey, CryptographyMode cryptographyMode)
        {
            this.CryptographyMode = cryptographyMode;
            KeyPassword = new Byte[32];
            Array.Copy(byteKey, KeyPassword, byteKey.Length > KeyPassword.Length ? KeyPassword.Length : byteKey.Length);
            byte[] byteIV = _encoding.GetBytes("!@#$&^*&'^*&^%$^%#)_)knljs89&(&$%$#$%)<:<:NJHFREZ?>>?");
            IV = new Byte[16];
            Array.Copy(byteIV, IV, byteIV.Length > IV.Length ? IV.Length : byteIV.Length);
        }

        protected string ByteToHexX2(byte[] bytes, string mark)
        {
            string returnstr = "";
            if (bytes != null)
                foreach (byte b in bytes)
                    returnstr += b.ToString("X2") + mark;

            return returnstr;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        public AESHelper(string key, CryptographyMode cryptographyMode)
            : this(_encoding.GetBytes(key), cryptographyMode)
        {
        }

        #region 成员变量
        internal byte[] KeyPassword
        {
            get;
            private set;
        }

        /// <summary>
        /// 运算模式
        /// </summary>
        private CipherMode _cipherMode = CipherMode.ECB;
        /// <summary>
        /// 填充模式
        /// </summary>
        private PaddingMode _paddingMode = PaddingMode.PKCS7;
        /// <summary>
        /// 字符串采用的编码
        /// </summary>
        private static Encoding _encoding = Encoding.UTF8;
        #endregion

        #region 辅助方法
        /// <summary>
        /// 将字符数组转换成字符串
        /// </summary>
        /// <param name="inputData"></param>
        /// <returns></returns>
        private string ConvertByteToString(byte[] inputData)
        {
            StringBuilder sb = new StringBuilder(inputData.Length * 2);
            foreach (var b in inputData)
            {
                sb.Append(b.ToString("X2"));
            }
            return sb.ToString();
        }

        /// <summary>
        /// 将字符串转换成字符数组
        /// </summary>
        /// <param name="inputString"></param>
        /// <returns></returns>
        private byte[] ConvertStringToByte(string inputString)
        {
            if (inputString == null || inputString.Length < 2)
            {
                throw new ArgumentException();
            }
            int l = inputString.Length / 2;
            byte[] result = new byte[l];
            for (int i = 0; i < l; ++i)
            {
                result[i] = Convert.ToByte(inputString.Substring(2 * i, 2), 16);
            }
            return result;
        }
        #endregion

        #region 加密
        /// <summary>
        /// AES加密
        /// </summary>
        /// <param name="Data">被加密的明文</param>
        /// <returns>密文</returns>
        public byte[] Encrypt(byte[] data)
        {
            try
            {
                if (data == null)
                    return null;
                if (CryptographyMode == CryptographyMode.ServiceProvider)
                {
                    return ServiceProviderEncrypt(data);
                }
                return DefaultEncrypt(data);
            }
            finally {
                data = null;
            }
        }

        byte[] DefaultEncrypt(byte[] inputData)
        {
            //byte[] Cryptograph = null; // 加密后的密文
            //Rijndael Aes = Rijndael.Create();
            if (inputData == null)
                throw new ArgumentException("invalid Argument!");
            try
            {
                using (Aes Aes = new AesManaged())
                {
                    // 开辟一块内存流
                    using (MemoryStream memory = new MemoryStream())
                    {
                        // 把内存流对象包装成加密流对象
                        using (CryptoStream Encryptor = new CryptoStream(memory, Aes.CreateEncryptor(KeyPassword, IV), CryptoStreamMode.Write))
                        {
                            // 明文数据写入加密流
                            Encryptor.Write(inputData, 0, inputData.Length);
                            Encryptor.FlushFinalBlock();
                            return memory.ToArray();
                        }
                    }
                }
            }
            catch
            {
                return null;
            }
            //return Cryptograph;
        }

        byte[] ServiceProviderEncrypt(byte[] inputData)
        {
            using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
            {
                aes.Key = KeyPassword;
                aes.Mode = _cipherMode;
                aes.Padding = _paddingMode;
                ICryptoTransform transform = aes.CreateEncryptor();
                byte[] data = transform.TransformFinalBlock(inputData, 0, inputData.Length);
                aes.Clear();
                return data;
            }
        }

        /// <summary>
        /// 加密字符串(加密为16进制字符串)
        /// </summary>
        /// <param name="inputString">要加密的字符串</param>
        /// <param name="password">密码</param>
        /// <returns></returns>
        public string Encrypt(string inputString)
        {
            byte[] toEncryptArray = _encoding.GetBytes(inputString);
            byte[] result = Encrypt(toEncryptArray);
            return ConvertByteToString(result);
        }

        /// <summary>
        /// 字符串加密(加密为16进制字符串)
        /// </summary>
        /// <param name="inputString">需要加密的字符串</param>
        /// <returns>加密后的字符串</returns>
        public string EncryptString(string inputString)
        {
            return Encrypt(inputString);
        }

        #endregion

        #region 解密

        /// <summary>
        /// AES解密
        /// </summary>
        /// <param name="Data">被解密的密文</param>
        /// <returns>明文</returns>
        public byte[] Decrypt(byte[] data)
        {
            try
            {
                if (CryptographyMode == CryptographyMode.ServiceProvider)
                {
                    return ServiceProviderDecrypt(data);
                }
                return DefaultDecrypt(data);
            }
            finally
            {
                data = null;
            }
        }

        byte[] ServiceProviderDecrypt(byte[] inputData)
        {
            using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
            {
                aes.Key = KeyPassword;
                aes.Mode = _cipherMode;
                aes.Padding = _paddingMode;
                ICryptoTransform transform = aes.CreateDecryptor();
                byte[] data = null;
                try
                {
                    data = transform.TransformFinalBlock(inputData, 0, inputData.Length);
                }
                catch
                {
                    return null;
                }
                aes.Clear();
                return data;
            }
        }
        byte[] DefaultDecrypt(byte[] inputData)
        {
            if (inputData == null)
                throw new ArgumentException("invalid Argument!");
            try
            {
                using (Aes Aes = new AesManaged())
                {
                    // 开辟一块内存流，存储密文
                    using (MemoryStream Memory = new MemoryStream(inputData))
                    {
                        // 把内存流对象包装成加密流对象
                        using (CryptoStream Decryptor = new CryptoStream(Memory, Aes.CreateDecryptor(KeyPassword, IV), CryptoStreamMode.Read))
                        {
                            // 明文存储区
                            using (MemoryStream originalMemory = new MemoryStream())
                            {
                                Byte[] Buffer = new Byte[1024];
                                Int32 readBytes = 0;
                                while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
                                {
                                    originalMemory.Write(Buffer, 0, readBytes);
                                }
                                return originalMemory.ToArray();
                            }
                        }
                    }
                }
            }
            catch
            {
                return null;
            }
        }
      
        /// <summary>
        /// 解密16进制的字符串为字符串
        /// </summary>
        /// <param name="inputString">要解密的字符串</param>
        /// <param name="password">密码</param>
        /// <returns>字符串</returns>
        public string Decrypt(string inputString)
        {
            byte[] toDecryptArray = ConvertStringToByte(inputString);
            string decryptString = _encoding.GetString(Decrypt(toDecryptArray));
            return decryptString;
        }

        /// <summary>
        /// 解密16进制的字符串为字符串
        /// </summary>
        /// <param name="inputString">需要解密的字符串</param>
        /// <returns>解密后的字符串</returns>
        public string DecryptString(string inputString)
        {
            return Decrypt(inputString);
        }

        #endregion

    }
}